﻿Imports Microsoft.VisualBasic
Imports Microsoft.VisualBasic.CompilerServices
Imports System
Imports System.Diagnostics
Imports System.Net
Imports System.Net.Sockets
Imports System.Runtime.CompilerServices

Namespace Stickam_Client
    Public Class SocketConnector
        ' Events
        Public Custom Event Closed As ClosedEventHandler
        Public Custom Event Connected As ConnectedEventHandler
        Public Custom Event ConnectFail As ConnectFailEventHandler
        Public Custom Event DataArrival As DataArrivalEventHandler

        ' Methods
        Public Sub Close()
            Me.CloseSocket(False)
        End Sub

        Private Sub CloseSocket(ByVal DoEvent As Boolean)
            If Me.SockRunning Then
                Me.SockRunning = False
                If Me.SockConnected Then
                    Me.SockConnected = False
                    Me.Sock.Shutdown(SocketShutdown.Both)
                End If
                Me.Sock.Close
            End If
            If DoEvent Then
                Dim closedEvent As ClosedEventHandler = Me.ClosedEvent
                If (Not closedEvent Is Nothing) Then
                    closedEvent.Invoke
                End If
            End If
        End Sub

        Public Sub Connect(ByVal IP As String, ByVal SocksIP As String, ByVal SocksVer As Integer, ByVal SocksEnabled As Boolean)
            If Not Me.SockRunning Then
                Dim str As String
                If SocksEnabled Then
                    str = SocksIP
                    Me.ServerIP = IP
                    Me.SocksAuthenticated = False
                    Me.SocksVersion = SocksVer
                Else
                    str = IP
                    Me.SocksAuthenticated = True
                End If
                Dim strArray2 As String() = Strings.Split(str, ":", -1, CompareMethod.Binary)
                Dim strArray As String() = Strings.Split(strArray2(0), ".", -1, CompareMethod.Binary)
                Dim address As Byte() = New Byte() { CByte(Conversions.ToInteger(strArray(0))), CByte(Conversions.ToInteger(strArray(1))), CByte(Conversions.ToInteger(strArray(2))), CByte(Conversions.ToInteger(strArray(3))) }
                Dim remoteEP As New IPEndPoint(New IPAddress(address), Conversions.ToInteger(strArray2(1)))
                Me.Sock = New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
                Me.SockRunning = True
                Me.Sock.BeginConnect(remoteEP, New AsyncCallback(AddressOf Me.ConnectCallback), Nothing)
            End If
        End Sub

        Private Sub ConnectCallback(ByVal ar As IAsyncResult)
            If Me.SockRunning Then
                Try 
                    Me.Sock.EndConnect(ar)
                    Me.SockConnected = True
                    If Me.SocksAuthenticated Then
                        Dim connectedEvent As ConnectedEventHandler = Me.ConnectedEvent
                        If (Not connectedEvent Is Nothing) Then
                            connectedEvent.Invoke
                        End If
                    Else
                        Me.SocksConnect
                    End If
                    Me.ReceiveData
                Catch exception1 As Exception
                    ProjectData.SetProjectError(exception1)
                    Dim exception As Exception = exception1
                    Me.ConnectionFailed
                    ProjectData.ClearProjectError
                End Try
            End If
        End Sub

        Private Sub ConnectionFailed()
            If Me.SockRunning Then
                Me.SockRunning = False
                If Me.SockConnected Then
                    Me.SockConnected = False
                    Me.Sock.Shutdown(SocketShutdown.Both)
                End If
                Me.Sock.Close
            End If
            Dim connectFailEvent As ConnectFailEventHandler = Me.ConnectFailEvent
            If (Not connectFailEvent Is Nothing) Then
                connectFailEvent.Invoke
            End If
        End Sub

        Private Function GetIPBytes(ByVal IP As String, ByVal SocksOrder As Integer) As String
            Dim strArray2 As String() = Strings.Split(IP, ":", -1, CompareMethod.Binary)
            Dim strArray As String() = Strings.Split(strArray2(0), ".", -1, CompareMethod.Binary)
            Dim num2 As Long = Conversions.ToLong(strArray2(1))
            Dim a As Double = Conversion.Fix(CDbl((CDbl(num2) / 256)))
            num2 = CLng(Math.Round(CDbl((num2 - (256 * a)))))
            Dim str2 As String = (Conversions.ToString(Strings.Chr(Conversions.ToInteger(strArray(0)))) & Conversions.ToString(Strings.Chr(Conversions.ToInteger(strArray(1)))) & Conversions.ToString(Strings.Chr(Conversions.ToInteger(strArray(2)))) & Conversions.ToString(Strings.Chr(Conversions.ToInteger(strArray(3)))))
            Dim str3 As String = (Conversions.ToString(Strings.Chr(CInt(Math.Round(a)))) & Conversions.ToString(Strings.Chr(CInt(num2))))
            If (SocksOrder = 4) Then
                Return (str3 & str2)
            End If
            Return (str2 & str3)
        End Function

        Private Sub HandleData(ByVal Data As String)
            If Me.SocksAuthenticated Then
                Dim dataArrivalEvent As DataArrivalEventHandler = Me.DataArrivalEvent
                If (Not dataArrivalEvent Is Nothing) Then
                    dataArrivalEvent.Invoke(Data)
                End If
            Else
                Dim connectedEvent As ConnectedEventHandler
                If (Me.SocksVersion = 4) Then
                    If (Strings.Asc(Strings.Mid(Data, 2, 1)) = 90) Then
                        Me.SocksAuthenticated = True
                        connectedEvent = Me.ConnectedEvent
                        If (Not connectedEvent Is Nothing) Then
                            connectedEvent.Invoke
                        End If
                    Else
                        Me.ConnectionFailed
                    End If
                ElseIf (Data.Length = 2) Then
                    If (Strings.Mid(Data, 1, 2) = ChrW(5) & ChrW(0)) Then
                        Me.SendData((ChrW(5) & ChrW(1) & ChrW(0) & ChrW(1) & Me.GetIPBytes(Me.ServerIP, 5)))
                    Else
                        Me.ConnectionFailed
                    End If
                ElseIf (Strings.Mid(Data, 1, 3) = ChrW(5) & ChrW(0) & ChrW(0)) Then
                    Me.SocksAuthenticated = True
                    connectedEvent = Me.ConnectedEvent
                    If (Not connectedEvent Is Nothing) Then
                        connectedEvent.Invoke
                    End If
                Else
                    Me.ConnectionFailed
                End If
            End If
        End Sub

        Private Sub ReceiveCallback(ByVal ar As IAsyncResult)
            If Me.SockRunning Then
                Try 
                    Dim count As Integer = Me.Sock.EndReceive(ar)
                    If (count = 0) Then
                        Me.CloseSocket(True)
                    Else
                        Me.HandleData(Globals.Encoder.GetString(Me.Buffer, 0, count))
                        Me.ReceiveData
                    End If
                Catch exception1 As Exception
                    ProjectData.SetProjectError(exception1)
                    Dim exception As Exception = exception1
                    Me.CloseSocket(True)
                    ProjectData.ClearProjectError
                End Try
            End If
        End Sub

        Private Sub ReceiveData()
            Me.Sock.BeginReceive(Me.Buffer, 0, Me.Buffer.Length, SocketFlags.None, New AsyncCallback(AddressOf Me.ReceiveCallback), Nothing)
        End Sub

        Public Sub SendData(ByVal Data As String)
            If (Me.SockRunning And Me.SockConnected) Then
                Me.Sock.Send(Globals.Encoder.GetBytes(Data))
            End If
        End Sub

        Private Sub SocksConnect()
            If (Me.SocksVersion = 4) Then
                Me.SendData((ChrW(4) & ChrW(1) & Me.GetIPBytes(Me.ServerIP, 4) & "Owner" & ChrW(0)))
            Else
                Me.SendData(ChrW(5) & ChrW(1) & ChrW(0))
            End If
        End Sub


        ' Fields
        Private Buffer As Byte() = New Byte(&H2801  - 1) {}
        Private LocalIP As IPAddress = Dns.Resolve(Dns.GetHostName).AddressList(0)
        Private ServerIP As String
        Private Sock As Socket
        Private SockConnected As Boolean = False
        Private SockRunning As Boolean = False
        Private SocksAuthenticated As Boolean
        Private SocksVersion As Integer

        ' Nested Types
        Public Delegate Sub ClosedEventHandler()

        Public Delegate Sub ConnectedEventHandler()

        Public Delegate Sub ConnectFailEventHandler()

        Public Delegate Sub DataArrivalEventHandler(ByVal Data As String)
    End Class
End Namespace

